home *** CD-ROM | disk | FTP | other *** search
- /*
- * load_s3m.c - Loads Scream Tracker III modules.
- *
- * (C) 1994 Mikael Nordqvist (d91mn@efd.lth.se, mech@df.lth.se)
- */
-
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <sys/soundcard.h>
- #include <sys/ultrasound.h>
- #include <limits.h>
- #include <string.h>
- #include <ctype.h>
-
- #include "mod.h"
-
- /* External global variables */
-
- SEQ_DECLAREBUF();
- extern int seqfd, gus_dev;
-
- extern struct mod_info M;
- extern struct options opt;
-
- extern char quit;
-
- extern char effect_used[NR_EFX];
-
- /* Local functions */
-
- static int process_header(int, unsigned long *, unsigned short *,
- unsigned char *);
- static int read_patterndata(int, unsigned short *, unsigned char *);
- static void fix_effect(unsigned char *, unsigned char *);
-
- /* Defines and variables used while loading module */
-
- #define S3M_HEADER_SIZE 0x60
- #define S3M_SAMPLEINFO_SIZE 0x50
-
- #define S3M_NAME 0x00
- #define S3M_TYPE 0x1d
- #define S3M_SONGLENGTH 0x20
- #define S3M_NOS 0x22
- #define S3M_PATTERNS 0x24
- #define S3M_FLAGS 0x26
- #define S3M_TRACKERVERSION 0x28
- #define S3M_FORMATVERSION 0x2a
- #define S3M_MAGIC 0x2c
- #define S3M_GLOBALVOLUME 0x30
- #define S3M_SPEED 0x31
- #define S3M_TEMPO 0x32
- #define S3M_MASTERVOLUME 0x33
- #define S3M_CHANNELS 0x40
-
- /* S3M_S_DATAPTR should be 0x0d and 24 bits but as the docs are a bit unclear
- * we use 0x0e and 16 bits. This will work for filesizes <= 1M.
- */
-
- #define S3M_S_TYPE 0x00
- #define S3M_S_DOSNAME 0x01
- #define S3M_S_DATAPTR 0x0e
- #define S3M_S_LEN 0x10
- #define S3M_S_LOOPSTART 0x14
- #define S3M_S_LOOPEND 0x18
- #define S3M_S_VOLUME 0x1c
- #define S3M_S_PACKED 0x1e
- #define S3M_S_FLAGS 0x1f
- #define S3M_S_C4SPD 0x20
- #define S3M_S_NAME 0x30
- #define S3M_S_MAGIC 0x4c
-
- #define S3M_VOLOPTIMIZATION 8
- #define S3M_FLAG_AMIGALIMITS 16
-
- #define S3M_S_FLAG_LOOPED 1
- #define S3M_S_FLAG_STEREO 2
- #define S3M_S_FLAG_16BIT 4
-
- static int version, expected_length;
-
- int load_s3m(int fd)
- {
- unsigned long sampledata_ptrs[256];
- unsigned short pattern_ptrs[256];
- unsigned char channels[32];
- char tmpbuf[80];
-
- int i, fatalerr;
-
- print_status("Loading S3M");
- M.nr_tracks=0;
-
- if(!process_header(fd, sampledata_ptrs, pattern_ptrs, channels))
- return 0;
-
- if(!read_patterndata(fd, pattern_ptrs, channels)) {
- info("Unable to read patterndata.\n");
- return 0;
- }
-
- ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
-
- info("\nUploading instruments to GUS...\n");
-
- fatalerr=0;
-
- for(i=1; i <= M.nr_samples; ++i) {
- if(M.sample[i].length > 4) { /* Only load non-silent samples */
- if(lseek(fd, sampledata_ptrs[i-1]*16, SEEK_SET) == -1) {
- info("Seek failed (%d)", sampledata_ptrs[i-1]*16);
- fatalerr=1;
- break;
- } else if(!read_and_upload_sample(fd, i)) {
- fatalerr=1;
- break;
- }
- }
- if(quit == QUIT_SIGNAL) /* Bail out if we got QUIT_SIGNAL */
- return 0;
- }
-
- if(fatalerr) {
- sprintf(tmpbuf, "Unable to load sample %d", i);
- print_status(tmpbuf);
- info("\nUnable to load sample %d.\n", i);
- sleep(1);
- return 0;
- }
-
- print_songname(M.name);
- print_samples(1);
- print_songinfo(M.nr_voices, M.nr_samples, "S3M", expected_length,
- M.songlength, M.nr_patterns);
- return 1;
- }
-
-
- static int process_header(int fd, unsigned long *sampledata_ptrs,
- unsigned short *pattern_ptrs, unsigned char *channels)
- {
- unsigned char buf[S3M_HEADER_SIZE];
- int tmp, i, silent_sample;
- struct sample_info *s;
- short s3m_flags;
- unsigned short sample_ptrs[256];
-
- if(read(fd, buf, S3M_HEADER_SIZE) != S3M_HEADER_SIZE) {
- info("Unable to read header from file.\n");
- return 0;
- }
- expected_length=S3M_HEADER_SIZE;
-
- if(strncmp("SCRM", &buf[S3M_MAGIC], 4)) {
- info("Not a Scream Tracker III module.\n");
- return 0;
- }
-
- if(buf[S3M_TYPE] != 16)
- info("Type-field in header does not equal ST3. Trying anyways.\n");
-
- tmp=*(short*)&buf[S3M_TRACKERVERSION];
- version=*(unsigned short *)&buf[S3M_FORMATVERSION];
- info("Tracker version %d.%0x Format version %d\n",
- (tmp>>8)&0x0f, tmp&0xff, version);
-
- strncpy(M.name, &buf[S3M_NAME], 0x1b);
- M.name[0x1b]=0;
- fix_string(M.name);
-
- /* Global / master volume currently ignored */
- opt.speed=buf[S3M_SPEED];
- opt.tempo=buf[S3M_TEMPO];
-
- M.songlength=*(short *)&buf[S3M_SONGLENGTH];
- M.nr_samples=*(short *)&buf[S3M_NOS];
- M.nr_patterns=*(short *)&buf[S3M_PATTERNS];
-
- if(!M.nr_samples || M.nr_samples > 255) {
- info("Invalid number of samples in module.\n");
- return 0;
- }
-
- if(M.nr_patterns > 255) {
- info("Invalid number of patterns.\n");
- return 0;
- }
-
- s3m_flags=*(short *)&buf[S3M_FLAGS];
- if(s3m_flags&S3M_FLAG_AMIGALIMITS) {
- opt.low_note=BASE_NOTE+3*12;
- opt.high_note=BASE_NOTE+6*12-1;
- info("Amiga limits.\n");
- }
- else {
- opt.low_note=BASE_NOTE+0*12;
- opt.high_note=BASE_NOTE+8*12-1;
- }
- if(s3m_flags&S3M_VOLOPTIMIZATION)
- info("Volume optimization flag set in module (not supported yet).\n");
-
- memcpy(channels, &buf[S3M_CHANNELS], 32);
- for(i=31; i >= 0 && channels[i] >= 16; --i) /* Channel <= 15 for samples */
- ;
- M.nr_voices=i+1;
- if(M.nr_voices <= 0 || M.nr_voices > 31) {
- info("Illegal number of voices (%d).\n", M.nr_voices);
- return 0;
- }
-
- if(read(fd, M.patterntable, M.songlength) != M.songlength) {
- info("Unable to read patterntable from file.\n");
- return 0;
- }
-
- if(read(fd, sample_ptrs, M.nr_samples*2) != M.nr_samples*2) {
- info("Unable to read sample-pointers from file.\n");
- return 0;
- }
-
- if(read(fd, pattern_ptrs, M.nr_patterns*2) != M.nr_patterns*2) {
- info("Unable to read pattern-pointers from file.\n");
- return 0;
- }
-
- expected_length+=M.songlength+M.nr_samples*2+M.nr_patterns*2;
-
- M.sample=(struct sample_info *)malloc((1+M.nr_samples)*
- sizeof(struct sample_info));
-
- print_sample_info_header();
-
- for(i=1; i <= M.nr_samples; ++i) {
- if(lseek(fd, sample_ptrs[i-1]*16, SEEK_SET) == -1 ||
- read(fd, buf, S3M_SAMPLEINFO_SIZE) != S3M_SAMPLEINFO_SIZE) {
- info("Unable to read sampleheader (%d).\n", i);
- return 0;
- }
- expected_length+=S3M_SAMPLEINFO_SIZE;
- s=&M.sample[i];
-
- /* Valid isn't set to TRUE until it's data has been read */
- bzero((void *)s, sizeof(struct sample_info));
-
- if(!strncmp("SCRI", &buf[S3M_S_MAGIC], 4)) {
- info("Sample %d is an Adlib instrument - skipped.\n", i);
- strcpy(s->name, "Adlib-instrument");
- continue;
- }
-
- silent_sample=0;
- if(strncmp("SCRS", &buf[S3M_S_MAGIC], 4)) {
- if(*(int *)&buf[S3M_S_MAGIC]) {
- info("Bad sampleheader (%d) magic = %d %d %d %d.\n", i,
- buf[S3M_S_MAGIC], buf[S3M_S_MAGIC+1], buf[S3M_S_MAGIC+2],
- buf[S3M_S_MAGIC+3]);
- strcpy(s->name, "Empty sample");
- continue;
- }
- silent_sample=1;
- }
-
- strncpy(s->name, &buf[S3M_S_NAME], 28);
- s->name[28]=0;
- fix_string(s->name);
- strncpy(s->dosname, &buf[S3M_S_DOSNAME], 12);
- s->dosname[12]=0;
- fix_string(s->dosname);
-
- s->length=*(unsigned long *)&buf[S3M_S_LEN];
- s->repeat_start=*(unsigned long *)&buf[S3M_S_LOOPSTART];
- s->repeat_end=*(unsigned long *)&buf[S3M_S_LOOPEND];
- s->volume=buf[S3M_S_VOLUME];
-
- expected_length+=s->length;
-
- if(buf[S3M_S_PACKED])
- info("Packed sample. Trying to load it as unpacked.\n");
-
- if((buf[S3M_S_FLAGS]&S3M_S_FLAG_LOOPED) &&
- s->repeat_end > s->repeat_start+4)
- s->looped=LOOP_FORWARD;
- else
- s->looped=0;
-
- if(buf[S3M_S_FLAGS]&S3M_S_FLAG_16BIT)
- s->bits_16=1;
- else
- s->bits_16=0;
-
- if(buf[S3M_S_FLAGS]&S3M_S_FLAG_STEREO)
- info("Stereo-sample. Trying to load it as a mono-sample.\n");
-
- s->repeat_start=MIN(MAX(0, s->repeat_start), s->length-2);
- s->repeat_end=MIN(MAX(0, s->repeat_end), s->length-
- (s->bits_16 ? 2 : 1));
-
- if(version == 2)
- s->unsigned_data=1;
-
- s->c2freq=*(long*)&buf[S3M_S_C4SPD];
-
- sampledata_ptrs[i-1]=(*(unsigned short*)&buf[S3M_S_DATAPTR]);
-
- if(silent_sample)
- s->length=0;
- print_sample_info(i);
- }
-
- for(i=0; i < M.nr_voices; ++i) {
- if(channels[i] <= 7)
- M.panning[i]=1; /* Left */
- else if(channels[i] <= 15)
- M.panning[i]=14; /* Right */
- else
- M.panning[i]=7; /* Center (adlib/unused) */
- }
-
- M.restartpos=0;
- M.volrange=64;
- opt.speed0stop=0; /* Ignored in S3M's */
-
- info("\nSamples: %d Patterns: %d Songlength: %d\n",
- M.nr_samples, M.nr_patterns, M.songlength);
-
- return 1;
- }
-
-
- static int read_patterndata(int fd, unsigned short *pattern_ptrs,
- unsigned char *channels)
- {
- unsigned char buf[3];
- struct event *e;
- int p, l, v;
- struct event events[64*32];
-
- for(v=0; v < NR_EFX; ++v)
- effect_used[v]=0;
-
- /* Allocate for worst case (no identical tracks) */
- M.tracks=(struct event **)malloc(M.nr_patterns*M.nr_voices*
- sizeof(struct event *));
-
- for(v=0; v < M.nr_voices; ++v)
- M.track_idx[v]=(int *)malloc(M.nr_patterns*sizeof(int));
-
- /* Add the empty track */
- bzero((void *)events, sizeof(struct event)*64);
- get_track_idx(events);
-
- for(p=0; p < M.nr_patterns; ++p) {
- bzero(events, 64*32*sizeof(struct event));
- if(lseek(fd, pattern_ptrs[p]*16, SEEK_SET) == -1)
- return 0;
- if(!get_bytes(fd, 0, 0))
- return 0;
- if(!get_bytes(fd, buf, 2)) /* Don't trust length-value */
- return 0;
- expected_length+=2;
- for(l=0; l < 64; ++l) {
- while(1) {
- if(!get_bytes(fd, buf, 1))
- return 0;
- expected_length++;
-
- if(!buf[0])
- break;
-
- e=&events[(buf[0]&0x1f)*64+l];
- if(buf[0]&32) {
- if(!get_bytes(fd, &buf[1], 2))
- return 0;
- expected_length+=2;
- switch(buf[1]) {
- case 255:
- e->note=0;
- break;
- case 254:
- e->note=NOTE_OFF;
- break;
- default:
- e->note=((buf[1]>>4)&0x0f)*12+(buf[1]&0x0f)+BASE_NOTE;
- }
- e->sample=buf[2];
- }
- if(buf[0]&64) {
- if(!get_bytes(fd, &buf[1], 1))
- return 0;
- expected_length++;
- e->effect2=EFX_VOLUME;
- e->arg2=buf[1];
- }
- if(buf[0]&128) {
- if(!get_bytes(fd, &buf[1], 2))
- return 0;
- expected_length+=2;
- e->effect=buf[1];
- e->arg=buf[2];
- }
- fix_effect(&e->effect, &e->arg);
- if(e->effect || (!e->effect && e->arg))
- effect_used[e->effect]=1;
- }
- }
- for(v=0; v < M.nr_voices; ++v)
- M.track_idx[v][p]=(channels[v] == 255 ? 0 :
- get_track_idx(&events[v*64]));
- }
- print_used_effects();
-
- return 1;
- }
-
- /* Converts the Scream Tracker effectnumbers to those used by the player.
- */
-
- static void fix_effect(unsigned char *effect, unsigned char *arg)
- {
- unsigned char e=*effect+'A'-1, a=*arg;
-
- if(!*effect) {
- *arg=0;
- return;
- }
-
- if(e < 'A' || e > 'Z') {
- #if 0
- debug("Invalid EFX (%d '%c'.\n", e, e);
- #endif
- *effect=*arg=0;
- return;
- }
-
- switch(e) {
- case 'A': e=EFX_SPEED; break;
- case 'B': e=EFX_JUMP; break;
- case 'C': e=EFX_BREAK; break;
- case 'D':
- if(!a) {
- e=EFX_VOLSLIDECONTINUE;
- }
- else if((a&0x0f) == 0x0f && a != 0x0f) {
- e=EFX_FINEVOLSLIDEUP;
- a=(a>>4)&0x0f;
- }
- else if((a&0xf0) == 0xf0 && a != 0xf0) {
- e=EFX_FINEVOLSLIDEDOWN;
- a&=0x0f;
- }
- else {
- e=EFX_VOLSLIDE;
- }
- break;
- case 'E':
- if(!a) {
- e=EFX_PORTADOWNCONTINUE;
- }
- else if(a >= 0xf0) {
- e=EFX_FINEPORTADOWN;
- a&=0x0f;
- }
- else if(a >= 0xe0) {
- e=EFX_EXTRAFINEPORTADOWN;
- a&=0x0f;
- }
- else {
- e=EFX_PORTADOWN;
- }
- break;
- case 'F':
- if(!a) {
- e=EFX_PORTAUPCONTINUE;
- }
- else if(a >= 0xf0) {
- e=EFX_FINEPORTAUP;
- a&=0x0f;
- }
- else if(a >= 0xe0) {
- e=EFX_EXTRAFINEPORTAUP;
- a&=0x0f;
- }
- else {
- e=EFX_PORTAUP;
- }
- break;
- case 'G': e=EFX_PORTANOTE; break;
- case 'H': e=EFX_VIBRATO; break;
- case 'I': e=EFX_TREMOR; break;
- case 'J': e=EFX_ARPEGGIO; break;
- case 'K': e=EFX_VIBRATOVOLSLIDE; break; /* "Retrig" for value 00 ?!?! */
- case 'L': e=EFX_PORTANOTEVOLSLIDE; break;
-
- case 'O': e=EFX_SAMPLEOFFSET_S3M; break;
- case 'Q': e=EFX_RETRIGGERVOLSLIDE; break;
- case 'R': e=EFX_TREMOLO; break;
- case 'S': e=EFX_EXTENDED; break;
- case 'T': e=EFX_TEMPO; break;
- case 'V': e=EFX_GLOBALVOLUME; break;
-
- default:
- e=a=0;
- }
-
- if(e == EFX_EXTENDED) {
- e=(a>>4)&0x0f;
- a&=0x0f;
- switch(e) {
- case 2:
- e=EFX_FINETUNE;
- a=(a-8)&0x0f;
- break;
- case 3: e=EFX_VIBWAVEFORM; break;
- case 4: e=EFX_TREMWAVEFORM; break;
- case 0x0b: e=EFX_LOOP; break;
- case 0x0c: e=EFX_NOTECUT; break;
- case 0x0d: e=EFX_NOTEDELAY; break;
- case 0x0e: e=EFX_PATTERNDELAY; break;
-
- /* Effects not implemented in ST3 */
- case 0: /* EFX_FILTER */
- case 1: /* EFX_GLISSANDO */
- case 0x0a: /* Previous Stereo-control, but remved in ST3 */
- case 0x0f: /* EFX_INVERTEDLOOP/EFX_FUNK */
- default:
- e=a=0;
- }
- }
-
- *arg=a;
- *effect=e;
- }
-